home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Visual Cafe 3
/
Visual Cafe 3.ISO
/
Vcafe
/
Source.bin
/
Calendar.java
< prev
next >
Wrap
Text File
|
1998-09-04
|
25KB
|
851 lines
package symantec.itools.awt.util;
import java.awt.Panel;
import java.awt.Dimension;
import java.awt.Color;
import java.awt.Font;
import java.awt.Rectangle;
import java.awt.Graphics;
import java.awt.FontMetrics;
import java.awt.Container;
import java.util.Date;
import java.text.DateFormat;
import symantec.itools.awt.ComboBox;
import symantec.itools.awt.util.spinner.NumericSpinner;
import java.beans.PropertyVetoException;
import java.beans.PropertyChangeListener;
import java.beans.VetoableChangeListener;
import java.awt.event.MouseEvent;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.AWTEvent;
import java.awt.AWTEventMulticaster;
import java.awt.LayoutManager;
// 01/29/97 TWB Integrated changes from Windows
// 01/30/97 RKM Changed invalidates to repaint
// Changed logic in paint
// 1)sc.setBackground is called, only if the colors are different
// 2)sc.paint is called instead of repaint
// Made setDate actually work
// 06/05/97 LAB Updated to Java 1.1. Changed most data members to protected, from private.
// 07/14/97 RKM Added empty override to setLayout, so users could not change it on us
// 07/18/97 LAB Added add/removeNotify to handle event listener registration.
// Updated version number to 1.1.
// 08/25/97 CAR getDate now returns a nicely formatted string
// 09/14/97 RKM Removed hard coded month strings
// 10/03/97 LAB Made paint use ComboBox's preferred size. This was preventing the ComboBox from
// displaying properly (Addresses Mac Bug #7491). Reworked the way the component
// lays its self out and paints itself in an attempt to make it less hard coded
// and more dynamic. Made paint use NumericSpinner's preferred size. This did
// away with the need for resize to be overridden.
/**
* Calendar component. Creates a graphic calendar displaying a month of dates.
* A combo box controls the month displayed, a numeric spin control changes the
* year displayed.
*
*
* @version 1.1, July 18, 1997
*
* @author Symantec
*
*/
public class Calendar extends Panel
{
/**
* Creates a default calendar with today's
* date initialized.
*/
public Calendar()
{
this(new Date());
}
/**
* Creates a calendar with the specified date initialized.
* @param defaultdate the date to be shown initially
*/
public Calendar(Date defaultdate)
{
super.setLayout(null);
bNeedsPlatformHelp = ComboBox.needsPlatformHelp(); // help comboBar change state
dCurrent = defaultdate;
dLast = dCurrent;
combo = new ComboBox();
try
{
java.text.DateFormatSymbols dfs = new java.text.DateFormatSymbols();
String months[] = dfs.getMonths();
//??? LAB ??? The last item is blank, why?
int length = months.length > 12 ? 12 : months.length;
for(int i = 0; i < length; ++i)
combo.addItem(months[i]);
}
catch(PropertyVetoException e) {}
add(combo);
sc = new NumericSpinner();
add(sc);
try
{
sc.setMin(1900);
sc.setMax(9999);
sc.setCurrent(1900 + dCurrent.getYear());
sc.setEditable(false);
combo.select( 0 );
combo.select(dCurrent.getMonth());
}
catch(PropertyVetoException e) {}
if (symantec.itools.lang.OS.isMacintosh())
selectedColor = Color.black;
else
selectedColor = Color.blue;
if (!symantec.itools.lang.OS.isMacintosh()) //12/18/96 Andy McFarland some platforms don't want their fonts hardwired...
{
if (bNeedsPlatformHelp) // different fonts to help display problems.
setFont(new Font("Dialog", Font.PLAIN, 9));
else
setFont(new Font("Dialog", Font.BOLD, 10));
}
}
/**
* Sets the date selected on the calendar.
* @param date the date that the calendar is to be set to
*
* @exception PropertyVetoException
* if the specified property value is unacceptable
*/
public void setDate(String date) throws PropertyVetoException
{
String oldValue = getDate();
if(!symantec.itools.util.GeneralUtils.objectsEqual(oldValue, date))
{
vetos.fireVetoableChange("Date", oldValue, date);
//Convert string to Date
Date newDate;
try {
newDate = new Date(date);
} catch (Exception e) {
newDate = new Date();
}
if (!dCurrent.equals(newDate))
{
dCurrent = newDate;
combo.select(dCurrent.getMonth());
sc.setCurrent(1900 + dCurrent.getYear());
//Trigger re calculation
firstDay = -1;
lastSelectedDate = -1;
repaint();
}
changes.firePropertyChange("Date", oldValue, date);
}
}
/**
* Returns the date that the calendar is set to.
* @return the current value of the calendar.
*/
public String getDate()
{
String str;
DateFormat df = DateFormat.getDateInstance(DateFormat.LONG);
str = df.format(dCurrent);
return str;
}
/**
* Sets the color used to highlight the selected date on the calendar.
* @param c the color that the selected color is to be set to
*
* @exception PropertyVetoException
* if the specified property value is unacceptable
*/
public void setSelectedColor(Color c) throws PropertyVetoException
{
Color oldValue = selectedColor;
if(!symantec.itools.util.GeneralUtils.objectsEqual(oldValue, c))
{
vetos.fireVetoableChange("SelectedColor", oldValue, c);
selectedColor = c;
repaint();
changes.firePropertyChange("SelectedColor", oldValue, c);
}
}
/**
* Returns the color that highlights the selected date on the calendar.
* @return the current selected color.
*/
public Color getSelectedColor()
{
return selectedColor;
}
/**
* Tells this component that it has been added to a container.
* This is a standard Java AWT method which gets called by the AWT when
* this component is added to a container. Typically, it is used to
* create this component's peer.
*
* It has been overridden here to hook-up event listeners.
*
* @see #removeNotify
*/
public synchronized void addNotify()
{
super.addNotify();
//Hook up listeners
if (mouse == null)
{
mouse = new Mouse();
addMouseListener(mouse);
}
if (action == null)
{
action = new Action();
sc.addActionListener(action);
combo.addActionListener(action);
}
}
/**
* Tells this component that it is being removed from a container.
* This is a standard Java AWT method which gets called by the AWT when
* this component is removed from a container. Typically, it is used to
* destroy the peers of this component and all its subcomponents.
*
* It has been overridden here to unhook event listeners.
*
* @see #addNotify
*/
public synchronized void removeNotify()
{
//Unhook listeners
if (mouse != null)
{
removeMouseListener(mouse);
mouse = null;
}
if (action != null)
{
sc.removeActionListener(action);
combo.removeActionListener(action);
action = null;
}
super.removeNotify();
}
/**
* Handles redrawing of this component on the screen.
* This is a standard Java AWT method which gets called by the Java
* AWT (repaint()) to handle repainting this component on the screen.
* The graphics context clipping region is set to the bounding rectangle
* of this component and its <0,0> coordinate is this component's
* top-left corner.
* Typically this method paints the background color to clear the
* component's drawing space, sets graphics context to be the foreground
* color, and then calls paint() to draw the component.
*
* It is overridden here to reduce flicker by eliminating the uneeded
* clearing of the background.
*
* @param g the graphics context
* @see java.awt.Component#repaint
* @see #paint
*/
public void update(Graphics g)
{
paint(g);
}
/**
* Paints this component using the given graphics context.
* This is a standard Java AWT method which typically gets called
* by the AWT to handle painting this component. It paints this component
* using the given graphics context. The graphics context clipping region
* is set to the bounding rectangle of this component and its <0,0>
* coordinate is this component's top-left corner.
*
* @param g the graphics context used for painting
* @see java.awt.Component#repaint
* @see #update
*/
public void paint(Graphics g)
{
//If the background of the sc is different, set it
if (!sc.getBackground().equals(getBackground()))
sc.setBackground(getBackground());
Dimension spinnerSize = sc.getPreferredSize();
sc.setBounds(getSize().width - spinnerSize.width - edgePad, edgePad, spinnerSize.width, spinnerSize.height);
FontMetrics fm = getFontMetrics(getFont());
Rectangle r = bounds();
if (bNeedsPlatformHelp)
{ // Solaris parameters
combo.reshape(12, 20, 100, 34);
//sc.reshape(r.width - 90, 20, 80, 34);
topRow = 70;
selectHeight = fm.getHeight() + 2;
centerNumber = 14;
heightAdjust = 6;
numberAdjust = 6;
}
else //Macintosh and Windows parameters
{
Dimension comboSize = combo.getPreferredSize();
combo.setBounds(edgePad, edgePad, comboSize.width, comboSize.height);
topRow = spinnerSize.height + edgePad + 2;
selectHeight = fm.getHeight();
centerNumber = 14;
heightAdjust = 2;
numberAdjust = 0;
}
int widthWith2EdgeAdj = r.width - (2 * edgePad);
int blockwidth = widthWith2EdgeAdj / 7; // 7 columns
int blockheight = ((r.height - topRow - edgePad) / 7); // 7 rows
int halfBlockwidth = blockwidth / 2;
int xloc = halfBlockwidth - (fm.stringWidth("S") / 2); //average location in block
int yloc = topRow + centerNumber;
if (dateSelectedx < 0)
{
int widthWithEdgeAdj = r.width - edgePad;
int topRowBlockHeight = topRow + (7 * blockheight);
// top
g.setColor(Color.black);
g.drawLine(edgePad, topRow, r.width - edgePad - 2, topRow);
// bottom
g.setColor(Color.lightGray);
g.drawLine(edgePad, topRowBlockHeight, widthWithEdgeAdj - 3, topRowBlockHeight);
g.setColor(Color.white);
g.drawLine(edgePad + 1, topRowBlockHeight + 1, widthWithEdgeAdj - 2, topRowBlockHeight + 1);
// left
g.setColor(Color.black);
g.drawLine(edgePad, topRow, edgePad, topRowBlockHeight);
// right
g.setColor(Color.lightGray);
g.drawLine(widthWithEdgeAdj - 2, topRow, widthWithEdgeAdj - 2, topRowBlockHeight);
g.setColor(Color.white);
g.drawLine(widthWithEdgeAdj - 1, topRow, widthWithEdgeAdj - 1, topRowBlockHeight);
// fill box
g.fillRect(edgePad + 1, topRow + blockheight , widthWith2EdgeAdj - 3, (6 * blockheight) );
g.setColor(Color.gray);
g.fillRect(edgePad + 1, topRow + 1, widthWith2EdgeAdj - 3, blockheight + 1 );
// letters on top
g.setColor(Color.white);
if (symantec.itools.lang.OS.isMacintosh())
{
yloc += (blockheight+1)/2 - fm.getHeight()/2; // Lets have the day labels track resizes
// in the component like the numbers do...
}
g.drawString("S", xloc, yloc - 1);
xloc = blockwidth + (halfBlockwidth - (fm.stringWidth("M") / 2));
g.drawString("M", xloc, yloc - 1);
xloc = (blockwidth * 2) + (halfBlockwidth - (fm.stringWidth("T") / 2));
g.drawString("T", xloc, yloc - 1);
xloc = (blockwidth * 3) + (halfBlockwidth - (fm.stringWidth("W") / 2));
g.drawString("W", xloc, yloc - 1);
xloc = (blockwidth * 4) + (halfBlockwidth - (fm.stringWidth("T") / 2));
g.drawString("T", xloc, yloc - 1);
xloc = (blockwidth * 5) + (halfBlockwidth - (fm.stringWidth("FM") / 2));
g.drawString("F", xloc, yloc - 1);
xloc = (blockwidth * 6) + (halfBlockwidth - (fm.stringWidth("S") / 2));
g.drawString("S", xloc, yloc - 1);
}
Date dMonthStart;
// must be run twice for Solaris
dMonthStart = new Date(dCurrent.getYear(), dCurrent.getMonth(), 1);
dMonthStart = new Date(dCurrent.getYear(), dCurrent.getMonth(), 1);
// which date does month begin?
if (firstDay < 0)
firstDay = dMonthStart.getDay();
int initdate = -1;
int caldate = 1;
int templastdate = -1;
int month = dCurrent.getMonth() + 1;
// which date already selected
if (lastSelectedDate < 1) initdate = dCurrent.getDate();
else if (dateSelectedx < 0) initdate = lastSelectedDate;
// paint calendar
g.setColor(Color.black);
calendar:
for (int j = 0; j < 6; j++)
{
for (int i = 0; i < 7; i++)
{
if ((maxMonthy < dateSelectedy) || (maxMonthy == dateSelectedy && maxMonthx < dateSelectedx))
{ //click after end date
dateSelectedx = -1;
dateSelectedy = -1;
break calendar;
}
if (j == 0 && i < firstDay)
{ // click before the start day
if (dateSelectedx == i && dateSelectedy == j)
{
dateSelectedx = -1;
dateSelectedy = -1;
break calendar;
}
else
continue;
}
// calculate where to draw
xloc = (i * blockwidth) + (halfBlockwidth - (fm.stringWidth(Integer.toString(caldate)) / 2));
yloc = topRow + heightAdjust + 4 + numberAdjust + (((j+2) * blockheight) - fm.getHeight());
// draw one digit numbers differently from two digit
if (dateSelectedx < 0 && initdate != caldate)
{
if (caldate < 10)
g.drawString((Integer.toString(caldate)), xloc , yloc);
else
g.drawString(Integer.toString(caldate), xloc , yloc);
}
else if (dateSelectedx == i && dateSelectedy == j || initdate == caldate)
{
if (dateSelectedx == i && dateSelectedy == j)
{
int tempYear = dCurrent.getYear();
int tempMonth = dCurrent.getMonth();
// must be run twice for Solaris
dCurrent = new Date();
dCurrent = new Date();
dCurrent.setDate(caldate);
dCurrent.setYear(tempYear);
dCurrent.setMonth(tempMonth);
}
//draw selected number
g.setColor(selectedColor);
if (caldate < 10)
g.fillRect(xloc, yloc - selectHeight + heightAdjust, fm.stringWidth(Integer.toString(caldate)) + 2, selectHeight);
else
g.fillRect(xloc, yloc - selectHeight + heightAdjust, fm.stringWidth(Integer.toString(caldate)), selectHeight);
g.setColor(Color.white);
if (caldate < 10)
g.drawString(Integer.toString(caldate), xloc , yloc);
else
g.drawString(Integer.toString(caldate), xloc , yloc);
templastdate = caldate;
g.setColor(Color.black);
}
else if (caldate == lastSelectedDate)
{ // unselected previously selected number
g.setColor(Color.white);
if (caldate < 10)
g.fillRect(xloc, yloc - selectHeight + heightAdjust, fm.stringWidth(Integer.toString(caldate)) + 2, selectHeight);
else
g.fillRect(xloc, yloc - selectHeight + heightAdjust, fm.stringWidth(Integer.toString(caldate)), selectHeight);
g.setColor(Color.black);
if (caldate < 10)
g.drawString(Integer.toString(caldate), xloc , yloc);
else
g.drawString(Integer.toString(lastSelectedDate), xloc, yloc);
lastSelectedDate = -1;
}
if (caldate >= 31)
{
maxMonthx = i;
maxMonthy = j;
break calendar;
}
else if ((caldate >= 30) && ((month == 4) || (month == 6) || (month == 9) || (month == 11)))
{
maxMonthx = i;
maxMonthy = j;
break calendar;
}
else if ((caldate >= 29) && (month == 2))
{
maxMonthx = i;
maxMonthy = j;
break calendar;
}
else if ((caldate >= 28) && (month == 2) && (!isLeapYear(dCurrent.getYear())))
{
maxMonthx = i;
maxMonthy = j;
break calendar;
}
caldate++;
}
}
if (lastSelectedDate < 0)
lastSelectedDate = templastdate;
dateSelectedx = -1;
dateSelectedy = -1;
if (!dLast.equals(dCurrent))
{
dLast = dCurrent;
sourceActionEvent();
}
}
/**
* Returns the recommended dimensions to properly display this component.
* This is a standard Java AWT method which gets called to determine
* the recommended size of this component.
*
* @return the preferred dimensions for the calender (250 x 200 pixels).
*
* @see #minimumSize
*/
public Dimension preferredSize()
{
return (new Dimension(250,200));
}
/**
* Returns the minimum dimensions to properly display this component.
* This is a standard Java AWT method which gets called to determine
* the minimum size of this component.
*
* @return the minimum dimensions needed for the calender (250 x 200 pixels).
*
* @see #preferredSize
*/
public Dimension minimumSize()
{
return (new Dimension(250,200));
}
/**
* Adds a listener for all property change events.
* @param listener the listener to add
* @see #removePropertyChangeListener
*/
public synchronized void addPropertyChangeListener(PropertyChangeListener listener)
{
changes.addPropertyChangeListener(listener);
}
/**
* Removes a listener for all property change events.
* @param listener the listener to remove
* @see #addPropertyChangeListener
*/
public synchronized void removePropertyChangeListener(PropertyChangeListener listener)
{
changes.removePropertyChangeListener(listener);
}
/**
* Adds a listener for all vetoable property change events.
* @param listener the listener to add
* @see #removeVetoableChangeListener
*/
public synchronized void addVetoableChangeListener(VetoableChangeListener listener)
{
vetos.addVetoableChangeListener(listener);
}
/**
* Removes a listener for all vetoable property change events.
* @param listener the listener to remove
* @see #addVetoableChangeListener
*/
public synchronized void removeVetoableChangeListener(VetoableChangeListener listener)
{
vetos.removeVetoableChangeListener(listener);
}
/**
* Sets the command name of the action event fired by this button.
* @param command The name of the action event command fired by this button
*
* @exception PropertyVetoException
* if the specified property value is unacceptable
*/
public void setActionCommand(String command) throws PropertyVetoException
{
String oldValue = actionCommand;
vetos.fireVetoableChange("ActionCommand", oldValue, command);
actionCommand = command;
changes.firePropertyChange("ActionCommand", oldValue, command);
}
/**
* @return the command name of the action event fired by this button.
*/
public String getActionCommand()
{
return actionCommand;
}
/**
* Adds the specified action listener to receive action events
* from this button.
* @param l the action listener
*/
public synchronized void addActionListener(ActionListener l)
{
actionListener = AWTEventMulticaster.add(actionListener, l);
}
/**
* Removes the specified action listener so it no longer receives
* action events from this button.
* @param l the action listener
*/
public synchronized void removeActionListener(ActionListener l)
{
actionListener = AWTEventMulticaster.remove(actionListener, l);
}
/**
* Fire an action event to the listeners
*/
public void sourceActionEvent()
{
if (actionListener != null)
actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, actionCommand));
}
/**
* This is the Mouse Event handling innerclass.
*/
class Mouse extends java.awt.event.MouseAdapter
{
/**
* Handles Mouse Pressed events
* @param e the MouseEvent
*/
synchronized public void mousePressed(MouseEvent e)
{
int topRow = -1;
int x = e.getX();
int y = e.getY();
if (bNeedsPlatformHelp) // Solaris machines have different drawing levels
{
topRow = 70;
centerNumber = 14;
heightAdjust = 6;
numberAdjust = 6;
}
else
{
topRow = 40; // needed for Windows drawing
numberAdjust = 0;
}
Rectangle r = bounds();
int blockheight = ((r.height - topRow - 14) / 7); // 7 rows
int blockwidth = (r.width - 32) / 7; // 7 columns
if ( ((x >= 15) && (x < r.width - 15)) && // if event in calendar window
((y >= topRow + blockheight) && (y < r.height - 14)))
{
dateSelectedx = ((x - 16) / blockwidth);
dateSelectedy = ((y - topRow - blockheight) / blockheight);
repaint();
firstDay = -1;
}
}
}
/**
* This is the Adjustment Event handling innerclass.
*/
class Action implements java.awt.event.ActionListener
{
/**
* Handles Action events
* @param e the ActionEvent
*/
public void actionPerformed(ActionEvent e)
{
Object target = e.getSource();
// ComboBox list items == month type events
if (target instanceof ComboBox)
{
int dy = dCurrent.getDate();
int mo = combo.getSelectedIndex();
int yr = dCurrent.getYear();
dCurrent = new Date(yr, mo, dy);
while (dCurrent.getMonth() != mo)
{
dCurrent = new Date(yr, mo, --dy);
lastSelectedDate = -1;
}
firstDay = -1;
repaint();
}
// Spin Control list items == year type events
if (target == sc)
{
int dy = dCurrent.getDate();
int mo = dCurrent.getMonth();
int yr = sc.getCurrent() - 1900;
dCurrent = new Date(yr, mo, dy);
while (dCurrent.getMonth() != mo)
{
dCurrent = new Date(yr, mo, --dy);
lastSelectedDate = -1;
}
firstDay = -1;
repaint();
}
}
}
/**
* Takes no action.
* This is a standard Java AWT method which gets called to specify
* which layout manager should be used to layout the components in
* standard containers.
*
* Since layout managers CANNOT BE USED with this container the standard
* setLayout has been OVERRIDDEN for this container and does nothing.
*
* @param lm the layout manager to use to layout this container's components
* (IGNORED)
* @see java.awt.Container#getLayout
**/
public void setLayout(LayoutManager lm)
{
}
boolean isLeapYear(int year)
{
if (year% 4 == 0 && (year != 2100))
return true;
else
return false;
}
String actionCommand;
ActionListener actionListener = null;
/**
* The ComboBox that displays/controls the month.
*/
protected ComboBox combo;
/**
* True if the ComboBox needs help changing state.
* @see #combo
* @see symantec.itools.awt.ComboBox#needsPlatformHelp
*/
protected boolean bNeedsPlatformHelp;
/**
* The spinner that displays/controls the year.
*/
protected NumericSpinner sc;
/**
* The date that the calendar is set to.
* @see #setDate
* @see #getDate
*/
protected Date dCurrent;
/**
* The current date when the Calendar was last painted.
*/
protected Date dLast;
/**
* The color used to highlight the selected date on the calendar.
* @see #setSelectedColor
* @see #getSelectedColor
*/
protected Color selectedColor;
/**
* Not used.
*/
protected String cal[][];
/**
* The number of pixels to offset the internal components from the edges.
*/
protected int edgePad = 2;
/**
* The column index of the currently selected day.
*/
protected int dateSelectedx = -1;
/**
* The row index of the currently selected day.
*/
protected int dateSelectedy = -1;
/**
* The day selected when the Caledar was last painted.
*/
protected int lastSelectedDate = -1;
/**
* The column index of the last day in the month.
*/
protected int maxMonthx = -1;
/**
* The row index of the last day in the month.
*/
protected int maxMonthy = -1;
/**
* The day of the week the month starts on.
*/
protected int firstDay = -1;
/**
* Internal drawing calibration value. The height of the topmost row of the Calendar, in pixels.
*/
protected int topRow = -1;
/**
* Internal drawing calibration value. The height of a selected area in the Calendar, in pixels.
*/
protected int selectHeight = -1;
/**
* Internal drawing calibration value.
*/
protected int centerNumber = -1;
/**
* Internal drawing calibration value.
*/
protected int heightAdjust = -1;
/**
* Internal drawing calibration value.
*/
protected int numberAdjust = -1;
private Action action = null;
private Mouse mouse = null;
private symantec.itools.beans.VetoableChangeSupport vetos = new symantec.itools.beans.VetoableChangeSupport(this);
private symantec.itools.beans.PropertyChangeSupport changes = new symantec.itools.beans.PropertyChangeSupport(this);
}